using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Kopernikus.Parsing;
using Kopernikus.Parsing.GenericLexing;
using Kopernikus.Parsing.GenericParsing;
using Kopernikus.Parsing.Lexing;

namespace Calc
{
    internal class Program
    {
        private static void Main()
        {
            GenericParser<Calculator, double> parser = GenericParser<Calculator, double>.Create(new Calculator());

            while (true)
            {
                Console.Write("Enter expression:");
                string line = Console.ReadLine();
                if (string.IsNullOrEmpty(line.Trim()))
                {
                    break;
                }
                try
                {
                    Console.WriteLine(parser.Parse(line));
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine(e.Message);
                }
            }
        }

        #region Nested type: Calculator

        [LexIgnore("\\s+")]
        public class Calculator
        {
            static Calculator()
            {
                InitializeFunctionMap();
            }

            #region Function map

            private static readonly Dictionary<double, UnaryFunctionDelegate> m_double2function =
                new Dictionary<double, UnaryFunctionDelegate>();

            private static readonly Dictionary<string, double> m_functionName2double =
                new Dictionary<string, double>(StringComparer.InvariantCultureIgnoreCase);

            private static void InitializeFunctionMap()
            {
                foreach (MethodInfo info in typeof (Math).GetMethods())
                {
                    try
                    {
                        UnaryFunctionDelegate function =
                            Delegate.CreateDelegate(typeof (UnaryFunctionDelegate), info) as UnaryFunctionDelegate;

                        int d = m_functionName2double.Count;
                        m_functionName2double[info.Name] = d;
                        m_double2function[d] = function;
                    }
                    catch
                    {
                    }
                }
            }

            private delegate double UnaryFunctionDelegate(double arg);

            #endregion

            [LexDefault(LexDefaultAttributeOptions.AnySingleCharacter)]
            private static double t_default(LexerMatch m)
            {
                // What ends up here are single characters such as (, ), +. - etc. The returned value
                // is not importants since all rule containing those symbols only considers the
                // values of the arguments.
                return 0;
            }

            [LexKeyword("e")]
            private static double t_e(LexerMatch m)
            {
                return Math.E;
            }

            [LexKeyword("pi")]
            private static double t_pi(LexerMatch m)
            {
                return Math.PI;
            }

            [Lex("num", "\\d+(\\.\\d+)?")]
            private static double t_num(LexerMatch m)
            {
                return double.Parse(m.Value, NumberStyles.Number, CultureInfo.GetCultureInfo(1033));
            }

            [Lex("id", "\\w+")]
            private static double t_id(LexerMatch m)
            {
                double functionId;
                if (m_functionName2double.TryGetValue(m.Value, out functionId))
                {
                    return functionId;
                }
                throw new ApplicationException("Unknown function: " + m.Value);
            }

            [CfgStart("Start -> Expression")]
            private static double p_main(double E)
            {
                return E;
            }

            [Cfg("Expression -> num")]
            [Cfg("Expression -> e")]
            [Cfg("Expression -> pi")]
            private static double p_parens(double num)
            {
                return num;
            }

            [Cfg("Expression -> id.0 ( Expression.1 )")]
            private static double p_fun(double fun, double arg)
            {
                UnaryFunctionDelegate function = m_double2function[fun];
                return function(arg);
            }


            [Cfg("Expression -> ( Expression )")]
            private static double p_parens(double[] args)
            {
                return args[1];
            }

            [Cfg("Expression -> Expression ^ Expression")]
            private static double p_exp(double[] args)
            {
                return Math.Pow(args[0], args[2]);
            }

            [Cfg("Expression -> Expression * Expression")]
            private static double p_mul(double[] args)
            {
                return args[0]*args[2];
            }

            [Cfg("Expression -> Expression / Expression")]
            private static double p_div(double[] args)
            {
                return args[0]/args[2];
            }

            [Cfg("Expression -> Expression + Expression")]
            private static double p_add(double[] args)
            {
                return args[0] + args[2];
            }

            [Cfg("Expression -> Expression - Expression")]
            private static double p_sub(double[] args)
            {
                return args[0] - args[2];
            }
        }

        #endregion
    }
}